/*****************************************
Module Float Arithmetic Computation Unit
opcode:
000:NOP
001:SQRT
010:FADD
011:FSUB
100:FMUL
101:FDIV
110:FMIN
111:FMAX
xxx:FMAC

*********************************************/
module fpu_s0
#
(
    parameter length_exp=11,
    length_val=52,
    msb_op=length_exp+length_val,
    length_mdiv=2**($clog2(length_val)),
    mdiv_dummy=length_mdiv-length_val
)
(
    input [msb_op:0]op1_i
    ,input [msb_op:0]op2_i
    ,input subsel
    `ifdef USE_FMCX
    ,input [length_mdiv-1:0]mul1_i
    ,input [length_mdiv-1:0]mul2_i
    ,input mext_sel
    `endif
    ,
    //input [1:0]mulwidsel,
    //Output Stage1
    output [length_val+2:0]addstage1_base_larger, //1b+val+2b for leading 1 and output rounding
    output [length_val+2:0]addstage1_base_smaller,
    output [length_exp:0]addstage1_exp,
    output fadd_signo,  //outputed sign
    output fadd_sub,      //perform subtraction in s2
    output fadd_much_larger,//abort further calculation and output fmax(abs)
    output fmax_abs,//1=op1,0=op2
    output [length_mdiv-1:0]mulstage1_LL,
    output [length_mdiv-1:0]mulstage1_LH,
    output [length_mdiv-1:0]mulstage1_HL,
    output [length_mdiv-1:0]mulstage1_HH,
    output mulstage1_sign,
    output [length_exp:0]mulstage1_exp,
    //IN CASE WE USE MULTICYCLE DIVIDER
    output div_sgn1,
    output [length_exp-1:0]div_exp1,
    output [length_val+1:0]div_divee,
    output [length_val+1:0]div_diver,
    //IN CASE WE USE MULTICYCLE SQRT
    output sqrt_sign1,
    output [length_exp-1:0]sqrt_exp1,
    output [length_val+1:0]sqrter,
    //Multicycle Div & Sqrt
    //output mulcycle_busy,
    //FMAX/MIN/CLASS output
    output fcalc_abort, //In case NaN, calculation result will be in here
    output [msb_op:0]fabort_o,
    output [msb_op:0]fmax_o,
    output [msb_op:0]fmin_o,
    output [9:0]fclass_o
);
wire sign1;
wire sign2;
wire [length_val-1:0]val1;
wire [length_val-1:0]val2;
wire [length_exp-1:0]exp1;
wire [length_exp-1:0]exp2;
wire [4:0]fprop1,fprop2;
defparam FPU_SLICER64.length_exp=length_exp;
defparam FPU_SLICER64.length_val=length_val;
fpu_slicer FPU_SLICER64
(
    .op1(op1_i),
    .op2(op2_i),
    .sign1(sign1),
    .sign2(sign2),
    .val1(val1),
    .val2(val2),
    .exp1(exp1),
    .exp2(exp2),
    .fclass_o(fclass_o),
    .fsignals1(fprop1), //{inf1,zero1,subn1,qnan1,snan1};
    .fsignals2(fprop2)  //{inf1,zero1,subn1,qnan1,snan1};

);

wire [63:0]mul_op1,mul_op2;
//FCMP
wire [1:0]exp_cmp; //00=expA<expB,01=expA<expB,1x=expA=expB(10=valA<valB)
wire [5:0]exp_diff;
wire [5:0]exp_dif1,exp_dif2;
wire [length_val+2:0]base_smaller;
wire [length_val+2:0]base_larger;

wire much_larger,diff_sym;
assign diff_sym=sign1^sign2;
assign exp_diff=exp_dif1-exp_dif2;
assign much_larger=(exp_diff>length_val);
//generate difference of exponential word
assign exp_cmp=(exp1==exp2)?((val1>val2)?2'b11:2'b10):
        (exp1>exp2)?2'b01:2'b00;//actually contains val compare
assign exp_dif1=(exp_cmp[0])?exp1:exp2;
assign exp_dif2=(!exp_cmp[0])?exp1:exp2;
assign eq=(op1==op2);
assign gt=(diff_sym)?((sign1==0)?1'b1:1'b0): //Condition:Different symbol
        ((sign1==0)? !exp_cmp[0] : exp_cmp[0]);  
assign fmax_o=(gt)?op1:op2;
assign fmin_o=(!gt)?op1:op2;
assign addstage1_exp=exp_dif1;
//FASUB-S1
wire fadd_signcalc;
assign base_smaller=(!exp_cmp[0])?{(!fprop1[2]),val1}:{(!fprop2[2]),val2};
defparam FADD_PreAligner64.IN_WIDTH = length_val+2;
right_bshifter FADD_PreAligner64
(
    .datain({base_smaller,2'b00}),
    .shiftnum(exp_diff[3:0]),
    .dataout(addstage1_base_smaller)
);
assign addstage1_base_larger=(exp_cmp[0])?  {(!fprop1[2]),val1,2'b00}:
                                            {(!fprop2[2]),val2,2'b00};
//运算真值表：  OP-A    OP-B    SUBSEL  OPERATE  A^B^C  SIGN
//              +       +       0       L+S       0     OP1
//              -       +       0       L-S       1     !(A>B)
//              +       -       0       L-S       1     (A>B)
//              -       -       0       L+S       0     OP1
//              +       +       1       L-S       1     (A>B)
//              -       +       1       L+S       0     OP1
//              +       -       1       L+S       0     OP1
//              -       -       1       L-S       1     !(A>B)
assign fadd_signo=(fadd_sub)?(!exp_cmp[0]):sign1;
assign fadd_sub=sign1^sign2^subsel;
//FMUL-S1
wire [length_mdiv-1:0]mul1_l,mul1_h,mul2_l,mul2_h;
`ifdef USE_FMCX
assign mul1_l=(mext_sel)?mul_op1[(length_mdiv/2)-1:0]:val1[(length_mdiv/2)-1:0];
assign mul1_h=(mext_sel)?mul_op1[length_mdiv-1:(length_mdiv+1)/2]:
            {{mdiv_dummy{1'b0}},val1[length_val-1:(length_val+1)/2]};
assign mul2_l=(mext_sel)?mul_op2[(length_mdiv/2)-1:0]:val2[(length_mdiv/2)-1:0];
assign mul2_h=(mext_sel)?mul_op2[length_mdiv-1:(length_mdiv+1)/2]:
            {{mdiv_dummy{1'b0}},val2[length_val-1:(length_val+1)/2]};
`else 
assign mul1_l=val1[(length_mdiv/2)-1:0];
assign mul1_h=val1[length_val-1:(length_val+1)/2];
assign mul2_l=val2[(length_mdiv/2)-1:0];
assign mul2_h=val2[length_val-1:(length_val+1)/2];
`endif
assign mulstage1_LL=mul1_l*mul2_l;
assign mulstage1_LH=mul1_l*mul2_h;
assign mulstage1_HL=mul1_h*mul2_l;
assign mulstage1_HH=mul1_h*mul2_h;

assign mulstage1_sign=sign1^sign2;
assign mulstage1_exp=exp1+exp2-{(length_exp-2){1'b1}};


//FDIV
assign div_sgn1=mulstage1_sign;
assign div_divee={(!fprop1[2]),val1};
assign div_divee={(!fprop2[2]),val2};
assign div_exp1=exp1-exp2+{1'b0,{length_exp-1{1'b1}}};

//FSQRT
assign sqrt_exp1=exp1>>1;
assign sqrt_sign1=sign1;
assign sqrter={(!fprop1[2]),val1};

//NaN/Zero/Inf Early Kick out
//FP Instruction early retire condition:
//add/minus a overwhelmingly large number by a small number
//calc with a NaN
//Multiply/Divide with 0/Inf 
//? MUL/DIV exp calc already overflow


endmodule
